package com.redepronik.negosys.rrhh.service;

import static com.redepronik.negosys.utils.Utils.presentaMensaje;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import javax.faces.application.FacesMessage;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.redepronik.negosys.rrhh.dao.ClienteDao;
import com.redepronik.negosys.rrhh.dao.PersonaDao;
import com.redepronik.negosys.rrhh.entity.Cliente;
import com.redepronik.negosys.rrhh.entity.Persona;
import com.redepronik.negosys.rrhh.entity.Tipocliente;
import com.redepronik.negosys.seguridad.entity.Rolusuario;
import com.redepronik.negosys.seguridad.entity.Usuario;
import com.redepronik.negosys.seguridad.service.RolService;
import com.redepronik.negosys.seguridad.service.UsuarioService;

@Service
public class ClienteServiceImpl implements ClienteService, Serializable {

	private static final long serialVersionUID = 1L;

	@Autowired
	private PersonaService personaService;

	@Autowired
	private TipoClienteService tipoClienteService;

	@Autowired
	private PersonaDao personaDao;

	@Autowired
	private ClienteDao clienteDao;

	@Autowired
	private UsuarioService usuarioService;

	@Autowired
	private RolService rolService;

	private ValidatorFactory factory = Validation
			.buildDefaultValidatorFactory();
	private Validator validator = factory.getValidator();

	public void actualizar(Persona persona) {
		Set<ConstraintViolation<Cliente>> violationsCliente = validator
				.validate(persona.getCliente());
		if (violationsCliente.size() > 0)
			for (ConstraintViolation<Cliente> cv : violationsCliente)
				presentaMensaje(FacesMessage.SEVERITY_INFO, cv.getMessage());
		else if (personaService.actualizar(persona))
			presentaMensaje(FacesMessage.SEVERITY_INFO,
					"CLIENTE ACTUALIZADO DE MANERA CORRECTA", "cerrar", true);
		else
			presentaMensaje(FacesMessage.SEVERITY_INFO, "EXISTE UN ERROR");
	}

	public Long contar() {
		return (Long) clienteDao.contar(Cliente.class, "clienteid");
	}

	public void eliminar(Persona persona) {
		persona.getCliente().setActivo(
				persona.getCliente().getActivo() ? false : true);
		personaDao.actualizar(persona);

		Usuario usuario = new Usuario();
		usuario = usuarioService.obtenerPorLogin(persona.getCedula());
		for (Rolusuario ru : usuario.getRolusuarios())
			if (ru.getRol().getNombre().compareTo("CLIE") == 0) {
				ru.setActivo(persona.getCliente().getActivo());
				usuarioService.actualizar(usuario);
				break;
			}
		if (persona.getCliente().getActivo())
			presentaMensaje(FacesMessage.SEVERITY_INFO,
					"SE ACTIVÓ AL CLIENTE: " + persona.getApellido() + " "
							+ persona.getNombre());
		else
			presentaMensaje(FacesMessage.SEVERITY_INFO,
					"SE DESACTIVÓ AL CLIENTE: " + persona.getApellido() + " "
							+ persona.getNombre());
	}

	public void insertar(Persona persona) {
		Tipocliente tipoCliente = tipoClienteService.obtenerPorTipoclienteId(2);
		persona.getCliente().setTipocliente(tipoCliente);
		persona.getCliente().setActivo(true);
		persona.getCliente().setPersona(persona);
		persona.getCliente().setFolio(String.valueOf(contar() + 1));
		Set<ConstraintViolation<Cliente>> violationsCliente = validator
				.validate(persona.getCliente());
		if (violationsCliente.size() > 0)
			for (ConstraintViolation<Cliente> cv : violationsCliente)
				presentaMensaje(FacesMessage.SEVERITY_INFO, cv.getMessage());
		else {
			boolean retorno = false;
			if (persona.getPersonaid() == null)
				retorno = personaService.insertar(persona);
			else
				retorno = personaService.actualizar(persona);
			if (retorno) {
				List<String> roles = new ArrayList<String>();
				roles.add("CLIE");
				usuarioService.insertarRoles(persona, roles);
				presentaMensaje(FacesMessage.SEVERITY_INFO,
						"CLIENTE INSERTADO DE MANERA CORRECTA", "cerrar", true);
			} else
				presentaMensaje(FacesMessage.SEVERITY_INFO, "EXISTE UN ERROR");
		}
	}

	public List<Persona> obtener() {
		List<Persona> lista = personaDao
				.obtenerPorHql(
						"select p from Persona p "
								+ "inner join fetch p.emailpersonas "
								+ "inner join fetch p.telefonopersonas "
								+ "inner join p.cliente c order by p.apellido, p.nombre",
						new Object[] {});
		return lista;
	}

	public List<Persona> obtener(String criterioClienteBusqueda) {
		criterioClienteBusqueda = criterioClienteBusqueda.toUpperCase();
		List<Persona> lista = new ArrayList<Persona>();
		if (criterioClienteBusqueda.length() >= 0
				&& criterioClienteBusqueda.length() <= 3) {
			presentaMensaje(FacesMessage.SEVERITY_ERROR,
					"INGRESE MAS DE 3 CARACTERES");
		} else {
			lista = personaDao
					.obtenerPorHql(
							"select p from Persona p inner join p.cliente c where (p.cedula like ?1 or p.nombre like ?1 or p.apellido like ?1) and p.activo=true and c.activo=true",
							new Object[] { "%" + criterioClienteBusqueda + "%" });
			if (lista.isEmpty()) {
				presentaMensaje(FacesMessage.SEVERITY_WARN,
						"NO SE ENCONTRO NINGUNA COINCIDENCIA");
			}
		}
		return lista;
	}

	public List<Persona> obtenerActivos() {
		List<Persona> lista = personaDao
				.obtenerPorHql(
						"select p from Persona p inner join p.cliente c where p.activo=true order by p.apellido, p.nombre",
						new Object[] {});
		return lista;
	}

	public List<Persona> obtenerGarante(String nombreCliente) {
		return personaDao
				.obtenerPorHql(
						"select p from Persona p inner join p.cliente c where (p.cedula like ?1 or p.nombre like ?1 or p.apellido like ?1) and p.activo=true and c.activo=true",
						new Object[] { "%" + nombreCliente + "%" });
	}

	public List<Persona> obtenerGarantesPorCredito(int creditoId) {
		return personaDao.obtenerPorHql(
				"select distinct p from Persona p inner join fetch p.cliente c "
						+ "left join fetch p.emailpersonas "
						+ "left join fetch p.telefonopersonas "
						+ "inner join fetch c.garantes g "
						+ "inner join g.credito cd where cd.creditoid=?1",
				new Object[] { creditoId });
	}

	public List<Persona> obtenerPersonasPorFacturasPendientes(int ciudadId,
			int vendedorId) {
		if (vendedorId == 0)
			return personaDao
					.obtenerPorHql(
							"select distinct p from Persona p "
									+ "inner join p.ciudad cd inner join p.cliente c "
									+ "inner join c.egresos e "
									+ "inner join e.tipodocumento td "
									+ "left join e.credito cr "
									+ "where cd.ciudadid=?1 and e.activo=true and e.pagado=false "
									+ "and cr.creditoid is null and td.tipodocumentoid=9",
							new Object[] { ciudadId });
		else
			return personaDao
					.obtenerPorHql(
							"select distinct p from Persona p "
									+ "inner join p.ciudad cd "
									+ "inner join p.cliente c inner join c.egresos e "
									+ "inner join e.vendedorid v "
									+ "inner join e.tipodocumento td "
									+ "left join e.credito cr "
									+ "where cd.ciudadid=?1 and e.activo=true and e.pagado=false "
									+ "and v.empleadocargoid=?2 and cr.creditoid is null "
									+ "and td.tipodocumentoid=9", new Object[] {
									ciudadId, vendedorId });
	}

	public Persona obtenerPorCedula(String cedula) {
		List<Persona> lista = personaDao.obtenerPorHql(
				"select distinct p from Persona p inner join p.cliente c "
						+ "left join fetch p.emailpersonas "
						+ "left join fetch p.telefonopersonas "
						+ "where p.cedula=?1 and p.activo=true",
				new Object[] { cedula });
		if (!lista.isEmpty())
			return lista.get(0);
		return null;
	}

	public List<Cliente> obtenerPorCiudad(int ciudadId) {
		return clienteDao.obtenerPorHql(
				"select distinct c from Cliente c inner join fetch c.persona p "
						+ "left join fetch p.emailpersonas "
						+ "left join fetch p.telefonopersonas "
						+ "inner join p.ciudad cd where cd.ciudadid=?1 "
						+ "order by c.nombrecomercial",
				new Object[] { ciudadId });
	}

	public Persona obtenerPorClienteId(Integer clienteId) {
		List<Persona> lista = personaDao.obtenerPorHql(
				"select distinct p from Persona p inner join p.cliente c "
						+ "left join fetch p.emailpersonas "
						+ "left join fetch p.telefonopersonas "
						+ "where c.clienteid=?1", new Object[] { clienteId });
		if (!lista.isEmpty())
			return lista.get(0);
		return null;
	}

	public Persona obtenerPorPersonaId(Integer personaId) {
		return personaService.obtenerPorPersonaId(personaId);
	}

	public List<Persona> obtenerTodosPorBusqueda(
			String criterioBusquedaCliente, int criterioBusquedaCiudad) {
		List<Persona> lista = null;

		if (criterioBusquedaCliente.compareToIgnoreCase("") == 0
				&& criterioBusquedaCiudad == 0)
			presentaMensaje(FacesMessage.SEVERITY_WARN,
					"INGRESE UN CRITERIO DE BUSQUEDA");
		else {
			if (criterioBusquedaCliente.length() >= 3
					|| criterioBusquedaCiudad != 0) {
				if (criterioBusquedaCliente.compareToIgnoreCase("") != 0
						&& criterioBusquedaCiudad != 0)
					lista = personaDao
							.obtenerPorHql(
									"select distinct p from Persona p "
											+ "inner join p.cliente c "
											+ "left join fetch p.emailpersonas "
											+ "left join fetch p.telefonopersonas "
											+ "inner join p.ciudad cd "
											+ "where (p.cedula like ?1 or p.nombre like ?1 or p.apellido like ?1 or c.nombrecomercial like ?1) "
											+ "and cd.ciudadid=?2 "
											+ "order by p.apellido, p.nombre",
									new Object[] {
											"%" + criterioBusquedaCliente + "%",
											criterioBusquedaCiudad });
				else if (criterioBusquedaCliente.compareToIgnoreCase("") != 0)
					lista = personaDao
							.obtenerPorHql(
									"select distinct p from Persona p "
											+ "inner join p.cliente c "
											+ "left join fetch p.emailpersonas "
											+ "left join fetch p.telefonopersonas "
											+ "where (p.cedula like ?1 or p.nombre like ?1 or p.apellido like ?1 or c.nombrecomercial like ?1) "
											+ "order by p.apellido, p.nombre",
									new Object[] { "%"
											+ criterioBusquedaCliente + "%" });
				else if (criterioBusquedaCiudad != 0)
					lista = personaDao.obtenerPorHql(
							"select distinct p from Persona p "
									+ "inner join p.cliente c "
									+ "left join fetch p.emailpersonas "
									+ "left join fetch p.telefonopersonas "
									+ "inner join p.ciudad cd "
									+ "where cd.ciudadid=?1 "
									+ "order by p.apellido, p.nombre",
							new Object[] { criterioBusquedaCiudad });
				if (lista.isEmpty())
					presentaMensaje(FacesMessage.SEVERITY_WARN,
							"NO SE ENCONTRO NINGUNA COINCIDENCIA");
			} else
				presentaMensaje(FacesMessage.SEVERITY_WARN,
						"INGRESE MINIMO 3 CARACTERES");
		}
		return lista;
	}
}